#ifndef _NET_H_
#define _NET_H_

#include <WinSock2.h>
#include <stdio.h>

#pragma comment( lib, "wsock32.lib" )

namespace car {

	class LASAddress {
	public:
		LASAddress() {
			mAddress = 0;
			mPort = 0;
		}

		LASAddress( unsigned char a, unsigned char b, unsigned char c, unsigned char d, unsigned short port ) {
			mAddress = a << 24 | b << 16 | c << 8 | d;
			mPort = port;
		}

		LASAddress( unsigned int address, unsigned short port ) {
			mAddress = address;
			mPort = port;
		}

		~LASAddress() {}

		unsigned int getAddress() const {
			return mAddress;
		}

		unsigned char getA() const {
			return (unsigned char)( mAddress >> 24 );
		}

		unsigned char getB() const {
			return (unsigned char)( mAddress >> 16 );
		}

		unsigned char getC() const {
			return (unsigned char)( mAddress >> 8 );
		}

		unsigned char getD() const {
			return (unsigned char)mAddress;
		}
		
		unsigned short getPort() const {
			return mPort;
		}

		bool operator == ( const LASAddress &other ) const {
			return mAddress == other.getAddress() && mPort == other.getPort();
		}

		bool operator != ( const LASAddress &other ) const {
			return mAddress != other.getAddress() || mPort != other.getPort();
		}

	private:
		unsigned int mAddress;
		unsigned short mPort;

	};

	inline bool initializeSockets() {
		WSAData wsaData;
		return WSAStartup( MAKEWORD(2, 2), &wsaData ) == NO_ERROR;
	}

	inline void shutdownSockets() {
		WSACleanup();
	}

	class LASSocket {
	public:
		LASSocket() {
			mSocket = 0;
		}

		~LASSocket() {
			close();
		}

		bool open( unsigned short port ) {
			mSocket = ::socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
			if( mSocket <= 0 ) {
				mSocket = 0;
				return false;
			}
			printf(" socket opened %d\n", mSocket );

			sockaddr_in address;
			address.sin_family = AF_INET;
			address.sin_addr.s_addr = INADDR_ANY;
			address.sin_port = htons( port );

			if( bind( mSocket, (const sockaddr *)&address, sizeof( sockaddr_in ) ) < 0 ) {
				close();
				return false;
			}

			DWORD nonBlocking = 1;
			if( ioctlsocket( mSocket, FIONBIO, &nonBlocking ) != 0 ) {
				close();
				return false;
			}

			return true;
		}

		void close() {
			if( mSocket != 0 ) {	
				printf(" socket closed %d\n", mSocket );
				closesocket( mSocket );
				mSocket = 0;
			}
		}

		bool isOpen() const {
			return mSocket != 0;
		}

		bool send( const LASAddress &destAddr, const void *data, int size ) {
			if( mSocket == 0 ) {
				return 0;
			}

			sockaddr_in address;
			address.sin_family = AF_INET;
			address.sin_addr.s_addr = htonl( destAddr.getAddress() );
			address.sin_port = htons( destAddr.getPort() );

			int sent_bytes = sendto( mSocket, (const char *)data, size, 0, (const sockaddr *)&address, sizeof( sockaddr_in ) );
			if( sent_bytes == -1 )
			{
				printf( "error %d\n",WSAGetLastError() ); 
			}
			return sent_bytes == size;
		}

		int receive( LASAddress &sender, void *data, int size ) {
			if( mSocket == 0 ) {
				return 0;
			}

			#if LAS_PLATFORM == LAS_PLATFORM_WIN32
			typedef int socklen_t;
			#endif

			sockaddr_in from;
			socklen_t fromLen = sizeof( from );

			int received_bytes = recvfrom( mSocket, (char *)data, size, 0, (sockaddr *)&from, &fromLen );

			if( received_bytes <= 0 ) {
				return 0;
			}

			unsigned int address = ntohl( from.sin_addr.s_addr );
			unsigned short port = ntohs( from.sin_port );
			
			sender = LASAddress( address, port );

			return received_bytes;
		}

	private:
		int mSocket;
	};

	class ReliableConnection {
	public:
		ReliableConnection()
		{
		}

		~ReliableConnection()
		{
		}

		bool start( unsigned short port )
		{			
			return mSocket.open( port );
		}

		void close()
		{
			mSocket.close();
		}

		bool sendPacket( const unsigned char data[], int size )
		{
			return mSocket.send( mAddress, &data[0], size );
		}

		int receivePacket( LASAddress & address, unsigned char data[], int size )
		{	
			int received_bytes = mSocket.receive( mAddress, &data[0], size );
			address = mAddress;
			return received_bytes;
		}

		void setAddress( LASAddress & addr )	{ mAddress = addr; }

	private:
		LASSocket	mSocket;
		LASAddress	mAddress;
	};

};

#endif